package top.jiangqiang.qianyi.common.exception;

import com.fasterxml.jackson.databind.JsonMappingException;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.exception.ExceptionUtil;
import org.dromara.hutool.core.exception.ValidateException;
import org.dromara.hutool.core.text.StrUtil;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.ErrorResponseException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.servlet.resource.NoResourceFoundException;
import top.jiangqiang.qianyi.common.base.http.ErrorStatus;
import top.jiangqiang.qianyi.common.base.model.response.Result;
import top.jiangqiang.qianyi.common.constants.http.*;

import java.util.List;
import java.util.Set;

/**
 * 全局异常处理器，将某些异常响应为json字符串
 *
 * @author jiangqiang
 */
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    private static void printLog(Throwable throwable) {
        log.error("{} {}", throwable.getClass(), ExceptionUtil.getMessage(throwable));
        log.error("详细异常信息：\n{}", ExceptionUtil.stacktraceToString(throwable, 30000));
    }

    /**
     * 处理所有应该返回400的异常，也可以分开处理，返回不同的响应信息，这里为了模糊错误信息，写到一起。
     *
     * @param exception
     */
    @ExceptionHandler(value = {
            BindException.class,//方法参数校验失败时抛出
            ConstraintViolationException.class,//上传文件时，文件为空
            HttpRequestMethodNotSupportedException.class,//请求方法错误
            ConversionFailedException.class,
            MissingServletRequestParameterException.class,
            MethodArgumentTypeMismatchException.class
    })
    public ResponseEntity<Result<?>> exceptionHandler(Exception exception) {
        printLog(exception);
        Error400Status badRequest = Error400Status.BAD_REQUEST;
        if (exception instanceof ConstraintViolationException constraintViolationException) {
            Set<ConstraintViolation<?>> constraintViolations = constraintViolationException.getConstraintViolations();
            if (CollUtil.isNotEmpty(constraintViolations)) {
                ConstraintViolation<?> constraintViolation = CollUtil.get(constraintViolations, 0);
                return processResult(ResponseEntity.status(badRequest.getRawStatusCode()).body(Result.error(badRequest, constraintViolation.getMessageTemplate())));
            }
        }
        Result<?> result = Result.error(badRequest, exception.getMessage());
        return processResult(ResponseEntity.status(badRequest.getRawStatusCode()).body(result));
    }

    @ExceptionHandler(value = {
            MethodArgumentNotValidException.class,//参数校验
    })
    public ResponseEntity<Result<?>> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException exception) {
        printLog(exception);
        Error400Status badRequest = Error400Status.BAD_REQUEST;
        List<FieldError> fieldErrors = exception.getFieldErrors();
        List<String> messages = fieldErrors.stream().map(DefaultMessageSourceResolvable::getDefaultMessage).toList();
        //data 返回完整错误信息，message返回一条用于页面提示
        Result<?> result = Result.error(badRequest, messages.getFirst(), messages);
        return processResult(ResponseEntity.status(badRequest.getRawStatusCode()).body(result));
    }

    @ExceptionHandler(value = {
            HttpMessageNotReadableException.class,//请求体缺失，json反序列化出现异常
    })
    public ResponseEntity<Result<?>> httpMessageNotReadableExceptionHandler(HttpMessageNotReadableException exception) {
        printLog(exception);
        Error400Status badRequest = Error400Status.BAD_REQUEST;
        Result<?> result = Result.error(badRequest, "请求参数错误");
        String message = exception.getMessage();
        if (StrUtil.isBlank(message)) {
            result = Result.error(badRequest, "请求参数错误");
        }
        if (message.startsWith("Required request body is missing:")) {
            result = Result.error(badRequest, "请求体缺失");
        }
        Throwable cause = exception.getCause();
        if (cause instanceof JsonMappingException jsonMappingException) {
            Throwable throwable = jsonMappingException.getCause();
            if (throwable instanceof Error400Exception error400Exception) {
                if (CollUtil.isNotEmpty(jsonMappingException.getPath())) {
                    result = Result.error(badRequest, "请求参数错误", error400Exception.getMessage() + ":" + jsonMappingException.getPath().getFirst().getFieldName());
                } else {
                    result = Result.error(badRequest, "请求参数错误", error400Exception.getMessage());
                }
            }
        }
        return processResult(ResponseEntity.status(badRequest.getRawStatusCode()).body(result));
    }

    @ExceptionHandler(value = {
            ValidateException.class,//手动校验字段不合法时抛出
    })
    public ResponseEntity<Result<?>> validateExceptionHandler(ValidateException exception) {
        printLog(exception);
        Error400Status badRequest = Error400Status.BAD_REQUEST;
        Result<?> result = Result.error(badRequest, exception.getMessage());
        return processResult(ResponseEntity.status(badRequest.getRawStatusCode()).body(result));
    }

    @ExceptionHandler(value = {
            NoResourceFoundException.class,
    })
    public ResponseEntity<Result<?>> noResourceFoundException(NoResourceFoundException exception) {
        printLog(exception);
        Error404Status notFound = Error404Status.NOT_FOUND;
        Result<?> result = Result.error(notFound);
        return processResult(ResponseEntity.status(notFound.getRawStatusCode()).body(result));
    }

    @ExceptionHandler(value = {
            MultipartException.class,
    })
    public ResponseEntity<Result<?>> multipartException(MultipartException exception) {
        printLog(exception);
        Error400Status badRequest = Error400Status.BAD_REQUEST;
        Result<?> result = Result.error(badRequest, "请求文件缺失");
        return processResult(ResponseEntity.status(badRequest.getRawStatusCode()).body(result));
    }

    @ExceptionHandler(value = {
            HttpMediaTypeNotSupportedException.class,
    })
    public ResponseEntity<Result<?>> httpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException exception) {
        printLog(exception);
        Error400Status badRequest = Error400Status.BAD_REQUEST;
        Result<?> result = Result.error(badRequest, exception.getMessage());
        return processResult(ResponseEntity.status(badRequest.getRawStatusCode()).body(result));
    }

    /**
     * 处理服务器内部错误或者其他异常
     */
    @ExceptionHandler(value = ErrorResponseException.class)
    public ResponseEntity<Result<?>> errorResponseExceptionHandler(ErrorResponseException errorResponseException) {
        printLog(errorResponseException);
        Result<?> result;
        HttpStatusCode statusCode = errorResponseException.getStatusCode();
        if (statusCode.is4xxClientError()) {
            ErrorStatus errorStatus;
            if (statusCode.isSameCodeAs(Error400Status.BAD_REQUEST.getHttpStatus())) {
                errorStatus = Error400Status.BAD_REQUEST;
            } else if (statusCode.isSameCodeAs(Error401Status.UNAUTHORIZED.getHttpStatus())) {
                errorStatus = Error401Status.UNAUTHORIZED;
            } else if (statusCode.isSameCodeAs(Error403Status.FORBIDDEN.getHttpStatus())) {
                errorStatus = Error403Status.FORBIDDEN;
            } else if (statusCode.isSameCodeAs(Error404Status.NOT_FOUND.getHttpStatus())) {
                errorStatus = Error404Status.NOT_FOUND;
            } else {
                errorStatus = Error400Status.BAD_REQUEST;
            }
            result = Result.error(errorStatus, errorResponseException.getMessage());
        } else {
            ErrorStatus errorStatus = Error500Status.INTERNAL_SERVER_ERROR;
            result = Result.error(errorStatus, errorResponseException.getMessage());
        }
        return processResult(ResponseEntity.status(statusCode).body(result));
    }

    /**
     * 处理业务异常
     */
    @ExceptionHandler(value = BaseException.class)
    public ResponseEntity<Result<?>> baseExceptionHandler(BaseException baseException) {
        printLog(baseException);
        Result<?> result = Result.error(baseException);
        return processResult(ResponseEntity.status(baseException.getStatusCode()).body(result));
    }

    /**
     * 默认异常处理逻辑
     */
    @ExceptionHandler(Throwable.class)
    public ResponseEntity<Result<?>> handleControllerException(Throwable throwable) {
        printLog(throwable);
        Error500Status internalServerError = Error500Status.INTERNAL_SERVER_ERROR;
        Result<?> result = Result.error(internalServerError, internalServerError.getMessage());
        return processResult(ResponseEntity.status(internalServerError.getRawStatusCode()).body(result));
    }

    /**
     * 统一处理响应结果，可以打印响应日志
     *
     * @param responseEntity
     * @return
     */
    private ResponseEntity<Result<?>> processResult(ResponseEntity<Result<?>> responseEntity) {
        return responseEntity;
    }
}
